package com.googlecode.dtools.typeinfo.dao.dfc;

/**
 * Created by IntelliJ IDEA.
 * User: A_Reshetnikov
 * Date: 07.06.11
 * Time: 11:22
 * To change this template use File | Settings | File Templates.
 */


import com.documentum.com.DfClientX;
import com.documentum.com.IDfClientX;
import com.documentum.fc.client.*;

import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfId;
import com.documentum.fc.common.IDfLoginInfo;
import com.googlecode.dtools.typeinfo.beans.DmType;
import com.googlecode.dtools.typeinfo.beans.DmTypeAttr;
import com.googlecode.dtools.typeinfo.dao.DmTypeDAO;


import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: A_Reshetnikov
 * Date: 19.11.2010
 * Time: 12:35:26
 * To change this template use File | Settings | File Templates.
 */
public class DmTypeDAOImplDFC implements DmTypeDAO {


    private String userName;
    private String userPassword;


    public String getDocbase() {
        return docbase;
    }

    private String docbase;
    private IDfSession session = null;
    private String docbaseId = null;
    private int possibleValuesCount;
    private int possibleValueLength = 1024;
    private boolean collectCountInfo;
    private boolean collectFKReferences;
    private boolean collectLastModifiedDate = false;
    private String labelNlsKey = "en";


    private IDfSession getSession() {
        IDfSession session = null;
        IDfClientX clientX = new DfClientX();
        IDfSessionManager sMgr = null;
        IDfLoginInfo loginInfo = clientX.getLoginInfo();
        loginInfo.setUser(userName);
        loginInfo.setPassword(userPassword);

        try {
            IDfClient client = clientX.getLocalClient();
            sMgr = client.newSessionManager();
            //bind the Session Manager to the login info
            sMgr.setIdentity(docbase, loginInfo);
            session = sMgr.getSession(docbase);
        } catch (DfException e) {
            e.printStackTrace();
        }
        return session;
    }

    public DmType getDmTypeBasicInfo(String typeName) {

        DmType dmType = new DmType();
        //     DmType dmType = new DmTypeImpl(null);
        //  DmType dmType = DmType.Factory.newInstance();
        try {
            if (session == null)
                session = getSession();
            IDfQuery q = new DfQuery();
            q.setDQL("SELECT name, super_name, attr_count, start_pos from dm_type where name='" + typeName + "'");
            IDfCollection col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                dmType.setName(typeName);
                dmType.setAttrCount(col.getInt("attr_count"));
                dmType.setStartPos(col.getInt("start_pos"));
                dmType.setSuperName(col.getString("super_name"));
            }

            // Count
            if (collectCountInfo) {
                String dql = "select count(*) from " + typeName;
                q.setDQL(dql);
                col = q.execute(session, DfQuery.DF_READ_QUERY);
                while (col.next()) {
                    dmType.setCount(col.getLong("count(*)"));
                }
            }

            // direct subtypes
            q.setDQL("SELECT name from dm_type where super_name='" + typeName + "'");
            col = q.execute(session, DfQuery.DF_READ_QUERY);
            List<String> list = new ArrayList<String>();
            while (col.next()) {
                list.add(col.getString("name"));
            }
            DmType.SubTypeList subTypeList = new DmType.SubTypeList();
            subTypeList.getSubType().addAll(list);
            dmType.setSubTypeList(subTypeList);


            // hierarchyLevel
            long hierarchyLevel = findHierarchyLevel(typeName);
            dmType.setHierarchyLevel(hierarchyLevel);


        } catch (DfException e) {
            e.printStackTrace();
        }


        return dmType;
    }

    private long findHierarchyLevel(String typeName) {
        String superName = findSuperNameByName(typeName);
        if (superName == null || superName.trim().equals(""))
            return 0;
        else
            return findHierarchyLevel(superName) + 1;

    }

    private String findSuperNameByName(String typeName) {
        String res = null;
        IDfQuery q = new DfQuery();
        q.setDQL("SELECT super_name from dm_type where name='" + typeName + "'");
        IDfCollection col = null;
        try {
            col = q.execute(session, DfQuery.DF_READ_QUERY);

            while (col.next()) {
                res = col.getString("super_name");
            }
        } catch (DfException e) {
            e.printStackTrace();
        }
        return res;
    }

    private void fillLastModifiedDate(DmType dmType) {

        if (dmType == null || dmType.getName() == null)
            return;

        dmType.setNote("");

        try {
            if (session == null)
                session = getSession();
            IDfQuery q = new DfQuery();
            q.setDQL(" select  max(r_modify_date) as v  from " + dmType.getName());
            // System.out.println("q.getDQL() = " + q.getDQL());
            IDfCollection col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                dmType.setLastModifiedDate(col.getString("v"));

            }
        } catch (DfException e) {
            System.out.println("OOps, " + dmType.getName() + " is not sysobject");
            //  e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }


    }

    public DmType getDmTypeInfo(String typeName) {
        DmType dmType = getDmTypeBasicInfo(typeName);

        if (collectLastModifiedDate) fillLastModifiedDate(dmType);

        List<DmTypeAttr> list = getAttrListInfo(typeName);
        DmType.AttributeList attributeList = new DmType.AttributeList();
        attributeList.getAttribute().addAll(list);
        dmType.setAttributeList(attributeList);

        return dmType;
    }

    public List<DmTypeAttr> getAttrListInfo(String typeName) {

        //IDfAttr iDfAttr;
        List<DmTypeAttr> list = new ArrayList<DmTypeAttr>();

        try {
            if (session == null)
                session = getSession();
            IDfQuery q = new DfQuery();
            q.setDQL("SELECT i_position, attr_name, attr_type, attr_repeating, attr_length" +
                    " \nFROM dm_type t" +
                    " where name='" + typeName + "'" +
                    " \n order by i_position desc");
            IDfCollection col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                DmTypeAttr dmTypeAttr = new DmTypeAttr();
                //  DmTypeAttr dmTypeAttr = DmTypeAttr.Factory.newInstance();
                dmTypeAttr.setName(col.getString("attr_name"));
                dmTypeAttr.setPos(-col.getInt("i_position"));
                dmTypeAttr.setTypeCode(col.getInt("attr_type"));
                dmTypeAttr.setLength(col.getInt("attr_length"));
                dmTypeAttr.setRepeating(col.getBoolean("attr_repeating"));
                list.add(dmTypeAttr);

            }
            System.out.println("Found  " + list.size() + " attribures in " + typeName + "\n Start fill additional info");

            // Заполнение доп информации у атрибута
            for (DmTypeAttr attr : list) {
                //  if (attr.getName().startsWith("ka"))
                fillAdditionalInfo(typeName, attr);
                System.out.print(".");
            }
            System.out.println("");

        } catch (DfException e) {
            e.printStackTrace();
        }


        return list;
    }
/*
    public DmRelationType getDmRelationType(String relationName) {

        DmRelationType r = new DmRelationType();
        IDfQuery q = new DfQuery();
        q.setDQL("SELECT t.parent_type, t.child_type," +
                " t.description, t.integrity_kind, " +
                "t.direction_kind, t.permanent_link, t.copy_child, t.security_type" +
                " from dm_relation_type t where t.relation_name='" + relationName + "'");

        if (session == null)
            session = getSession();
        try {
            IDfCollection col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                r.setName(relationName);
                r.setChildType(col.getString("child_type"));
                r.setParentType(col.getString("parent_type"));
                r.setDescription(col.getString("description"));
                r.setDirection(col.getInt("direction_kind"));
                r.setIntegrity(col.getInt("integrity_kind"));
                r.setPermanentLink(col.getBoolean("permanent_link"));
                r.setSecurityType(col.getString("security_type"));
            }


            q.setDQL("select count(*) as cnt from dm_relation where relation_name='" + relationName + "'");
            col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                r.setCount(col.getLong("cnt"));
            }

            fillAdditionalInfoAboutParentsAndChildren(r, true);
            fillAdditionalInfoAboutParentsAndChildren(r, false);
            fillAttributePossibleValues(r, "description");
            fillAttributePossibleValues(r, "effective_date");
            fillAttributePossibleValues(r, "expiration_date");
            fillAttributePossibleValues(r, "order_no");
            fillAttributeCount(r, "description");
            fillAttributeCount(r, "effective_date");
            fillAttributeCount(r, "expiration_date");
            fillAttributeCount(r, "order_no");


        } catch (DfException e) {
            e.printStackTrace();
        }


        return r;

    }    */
    /*
  private void fillAttributePossibleValues(DmRelationType r, String attrName) throws DfException {
      if (session == null)
          session = getSession();

      List<String> list = new ArrayList<String>();
      String dql =
              "select distinct " + attrName + "  from dm_relation where relation_name='" + r.getName() + "'  ENABLE (RETURN_TOP" + possibleValuesCount + " )";
      IDfCollection col;
      IDfQuery q = new DfQuery();
      q.setDQL(dql);
      col = q.execute(session, DfQuery.DF_READ_QUERY);
      while (col.next()) {
          list.add(col.getString(attrName));
      }
      if (attrName.equals("description"))
          r.setDescriptionList(list);
      if (attrName.equals("effective_date"))
          r.setEffectiveDateList(list);
      if (attrName.equals("expiration_date"))
          r.setExpirationDateList(list);
      if (attrName.equals("order_no"))
          r.setOrderNoList(list);

  }  */

/*

    private void fillAttributeCount(DmRelationType r, String attrName) throws DfException {
        if (session == null)
            session = getSession();

        Long cnt = 0L;
        String dql =
                "select count(distinct " + attrName + ") as cnt  from dm_relation where relation_name='" + r.getName() + "'  ENABLE (RETURN_TOP" + possibleValuesCount + " )";
        IDfCollection col;
        IDfQuery q = new DfQuery();
        q.setDQL(dql);
        col = q.execute(session, DfQuery.DF_READ_QUERY);
        while (col.next()) {
            cnt = col.getLong("cnt");
        }
        if (attrName.equals("description"))
            r.setDescriptionCount(cnt);
        if (attrName.equals("effective_date"))
            r.setEffectiveDateCount(cnt);
        if (attrName.equals("expiration_date"))
            r.setExpirationDateCount(cnt);
        if (attrName.equals("order_no"))
            r.setOrderNoCount(cnt);

    }

*/
/*

    private void fillAdditionalInfoAboutParentsAndChildren(DmRelationType dmRelationType, boolean parent) throws DfException {


        if (session == null)
            session = getSession();

        List<String> list = new ArrayList<String>();

        String dql =
                "select distinct  r_object_type \n" +
                        "from dm_sysobject \n" +
                        "where r_object_id in (\n" +
                        "SELECT " + (parent ? "parent_id" : "child_id") + " \n" +
                        "from dm_relation r where r.relation_name = '" +
                        dmRelationType.getName()
                        + "' \n"
                        // + "where "+ attr.getName()+" \n" +
                        //  "is not null"
                        + ")";
        IDfCollection col;
        IDfQuery q = new DfQuery();
        q.setDQL(dql);
        col = q.execute(session, DfQuery.DF_READ_QUERY);
        while (col.next()) {
            list.add(col.getString("r_object_type"));
        }
        if (parent)
            dmRelationType.setParents(list);
        else
            dmRelationType.setChildren(list);

    }
*/

    public List<String> getRelationTypesNamesList() {
        List<String> list = new ArrayList<String>();
        IDfQuery q = new DfQuery();
        q.setDQL("select relation_name from dm_relation_type where relation_name like 'k%'");
        if (session == null)
            session = getSession();

        try {


            IDfCollection col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                list.add(col.getString("relation_name"));
            }

        } catch (DfException e) {
            e.printStackTrace();
        }

        return list;

    }

    public Map<String, String> getTypeTagMap(List<String> typesList) throws DfException {
        if (session == null)
            session = getSession();
        String tag = null;

        Map<String, String> map = new HashMap<String, String>();


        for (String typeName : typesList) {

            String dql = "select distinct substr(r_object_id,0,2) as tag_ from " + typeName;
            IDfQuery q = new DfQuery();
            q.setDQL(dql);
            IDfCollection col = q.execute(session, DfQuery.DF_READ_QUERY);
            if (col.next()) {
                tag = col.getString("tag_");
            }
            while (col.next()) {
                System.err.println("more then 2 tag of object type!" + typeName);
            }
            //  if (!tag.equals("00"))
            map.put(typeName, tag);

        }
        return map;
    }

    public List<String> getTypeNameList() {
        List<String> list = new ArrayList<String>();
        IDfQuery q = new DfQuery();
        q.setDQL("select name from dm_type");
        if (session == null)
            session = getSession();
        try {
            IDfCollection col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                list.add(col.getString("name"));
            }

        } catch (DfException e) {
            e.printStackTrace();
        }
        return list;
    }

    public boolean testConnection() {
        if (session == null)
            session = getSession();
        IDfQuery q = new DfQuery();
        q.setDQL("select 1 from dm_location");
        try {
            q.execute(session, DfQuery.READ_QUERY);
        } catch (Exception e) {
            return false;
        }
        return true;
    }


    private void fillAdditionalInfo(String typeName, DmTypeAttr attr) throws DfException {

        if (session == null)
            session = getSession();
        if (attr.getName().equals("user_password")) return;
        IDfQuery q = new DfQuery();
        IDfCollection col;
        if (collectCountInfo) {
            q.setDQL("SELECT count(distinct " + attr.getName() + ") as cnt from " + typeName);
            col = q.execute(session, DfQuery.DF_READ_QUERY);
            while (col.next()) {
                attr.setDistinctCount(col.getLong("cnt"));

            }
        }

        // PossValuesList
        if (possibleValuesCount > 0) {
            if (collectCountInfo && attr.getDistinctCount() > 0) {
                fillPossibleValuesList(typeName, attr);

            }
        }

        if (collectFKReferences) {
            fillFKReference(typeName, attr);
        }

        //Label

        q.setDQL("SELECT label_text " +
                "FROM dmi_dd_attr_info " +
                "WHERE type_name='" + typeName +
                "' and nls_key = '" + labelNlsKey + "'" +
                "and attr_name='" + attr.getName() + "'");
        col = q.execute(session, DfQuery.DF_READ_QUERY);
        while (col.next()) {
            attr.setLabel(col.getString("label_text"));
        }

    }


    //todo

    private void fillFKReference(String typeName, DmTypeAttr attr) throws DfException {


        // Description
        if (attr.getTypeCode() == 3 ||
                (attr.getTypeCode() == 2 && attr.getLength() == 32)) {

            //some exclusions
            if (typeName.equals("kc_filenomenclature_node") && attr.getName().equals("ka_dept_code"))
                return;
            if (attr.getName().startsWith("i_")
                    || attr.getName().startsWith("a_")
                    || attr.getName().startsWith("r_")
                    || attr.getName().startsWith("acl")
                    || attr.getName().equals("owner_name")
                    || attr.getName().equals("group_name")
                    || attr.getName().equals("resolution_label"))
                return;
            System.out.println("\nfill FK reference for " + typeName + "." + attr.getName());

            Set<String> set = getFKasSysobjectChilds(typeName, attr);

            set.addAll(getFKbyTagRecognition(typeName, attr));

            if (set.size() > 0) {
                DmTypeAttr.FkReferenceList fkList = new DmTypeAttr.FkReferenceList();
                fkList.getFkReference().addAll(set);
                attr.setFkReferenceList(fkList);
            }

        }

    }

    private Set<String> findFK4NullParentbyDfcGetType(String typeName, DmTypeAttr attr) throws DfException {
        if (session == null)
            session = getSession();
        List<String> list = new ArrayList<String>();
        Set<String> typeSet = new HashSet<String>();
        String dql = String.format("select distinct %s as tag_ from %s enable  (RETURN_TOP  5)", attr.getName(), typeName);
        IDfCollection col;
        IDfQuery q = new DfQuery();
        q.setDQL(dql);
        col = q.execute(session, DfQuery.DF_READ_QUERY);
        while (col.next()) {
            list.add(col.getString("tag_"));
        }
        for (String id : list) {
            String type = findTypeWithDfc(id);
            if (type != null)
                typeSet.add(type);
        }
        return typeSet;

    }

    private String findTypeWithDfc(String id) {
        if (session == null)
            session = getSession();
        if (id == null || "".equals(id) || DfId.DF_NULLID_STR.equals(id))
            return null;
        try {
            IDfPersistentObject persistentObject = session.getObject(new DfId(id));
            return persistentObject.getType().getName();
        } catch (DfIdNotFoundException e) {
            return null;
        } catch (DfException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        return null;
    }

    private Set<String> getFKbyTagRecognition(String typeName, DmTypeAttr attr) throws DfException {
        if (session == null)
            session = getSession();

        if (docbaseId == null) {
            docbaseId = Long.toHexString(Long.valueOf(session.getDocbaseId())).toLowerCase();
        }

        if (attr.getName().equals("ka_oms_id")) return null;

        List<String> tagList = new ArrayList<String>();
        Set<String> set = new HashSet<String>();
        String dql = String.format(
                "SELECT distinct %s as tag_ " +
                        "from %s " +
                        "where %s %s like '___%s%%' enable (RETURN_TOP  50)",
                attr.getName(),
                typeName,
                attr.isRepeating() ? "any" : "",
                attr.getName(),
                docbaseId);

        IDfCollection col;
        IDfQuery q = new DfQuery();
        q.setDQL(dql);
        col = q.execute(session, DfQuery.DF_READ_QUERY);
        while (col.next()) {
            tagList.add(col.getString("tag_"));
        }
        for (String tag : tagList) {
            set.add(findTypeWithDfc(tag));
        }
        return set;
    }

    private Set<String> getFKasSysobjectChilds(String typeName, DmTypeAttr attr) throws DfException {
        if (session == null)
            session = getSession();

        Set<String> hashSet = new HashSet<String>();

        String dql =
                "select distinct r_object_type \n" +
                        "from dm_sysobject \n" +
                        "where r_object_id in (\n" +
                        "SELECT " + attr.getName() + " \n" +
                        "from " + typeName + " \n"
                        // + "where "+ attr.getName()+" \n" +
                        //  "is not null"
                        + ")";
        IDfCollection col;
        IDfQuery q = new DfQuery();
        q.setDQL(dql);
        col = q.execute(session, DfQuery.DF_READ_QUERY);
        while (col.next()) {
            hashSet.add(col.getString("r_object_type"));
        }
        return hashSet;
    }


    private void fillPossibleValuesList(String typeName, DmTypeAttr attr) throws DfException {
        if (session == null)
            session = getSession();
        IDfQuery q = new DfQuery();
        IDfCollection col;
        String dql = String.format("SELECT distinct  substr(%s,0,%d) as v from %s ENABLE (RETURN_TOP %d)",
                attr.getName(),
                possibleValueLength,
                typeName,
                possibleValuesCount);
        q.setDQL(dql);
        col = q.execute(session, DfQuery.DF_READ_QUERY);
        List<String> list = new ArrayList<String>();
        String s;
        while (col.next()) {
            s = col.getString("v");
            if (s != null && s.length() != 0 && !s.equals(" ")) {
                s = s.replaceAll("//", "/ /");
                list.add(s);
            }
        }
        DmTypeAttr.PossValuesList possValuesList = new DmTypeAttr.PossValuesList();
        possValuesList.getPossValue().addAll(list);
        attr.setPossValuesList(possValuesList);
    }


    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

    public void setDocbase(String docbase) {
        this.docbase = docbase;
    }

    public void setPossibleValuesCount(int possibleValuesCount) {
        this.possibleValuesCount = possibleValuesCount;
    }

    public void setCollectCountInfo(boolean collectCountInfo) {
        this.collectCountInfo = collectCountInfo;
    }

    public void setLabelNlsKey(String labelNlsKey) {
        this.labelNlsKey = labelNlsKey;
    }

    public void setCollectFKReferences(boolean collectFKReferences) {
        this.collectFKReferences = collectFKReferences;
    }

    public void setPossibleValueLength(int possibleValueLength) {
        this.possibleValueLength = possibleValueLength;
    }

    public void setCollectLastModifiedDate(boolean collectLastModifiedDate) {
        this.collectLastModifiedDate = collectLastModifiedDate;
    }
}
